package controller.arithmethicParser;

import java.io.Reader;

import model.ParseError;
import model.arithmeticExpression.tokens.BinOp;
import model.arithmeticExpression.tokens.BinOpExp;
import model.arithmeticExpression.tokens.DivOp;
import model.arithmeticExpression.tokens.Exp;
import model.arithmeticExpression.tokens.MinOp;
import model.arithmeticExpression.tokens.NumExp;
import model.arithmeticExpression.tokens.PlusOp;
import model.arithmeticExpression.tokens.TimesOp;

/**
 * @author Sjaak Smetsers
 * @version 1.0 -- 06-03-2012
 */
public class ArithmeticTRparser {
	private ArithmeticTokenizer	tokens;
	
	public ArithmeticTRparser(Reader input) throws ParseError {
		tokens = new ArithmeticTokenizer(input);
	}
	
	public Exp parse() throws ParseError {
		Exp e = parseExp();
		if (tokens.currentToken() == ArithmeticTokenizer.ArithmeticToken.EOFToken) {
			return e;
		} else {
			throw new ParseError("End of input expected", tokens.currentToken().toString());
		}
	}
	
	public BinOp parseHighPrioOp() throws ParseError {
		switch (tokens.currentToken()) {
			case TimesToken:
				tokens.nextToken();
				return new TimesOp();
			case DivToken:
				tokens.nextToken();
				return new DivOp();
			default:
				return null;
		}
	}
	
	public BinOp parseLowPrioOp() throws ParseError {
		switch (tokens.currentToken()) {
			case PlusToken:
				tokens.nextToken();
				return new PlusOp();
			case MinusToken:
				tokens.nextToken();
				return new MinOp();
			default:
				return null;
		}
	}
	
	public Exp parseExp() throws ParseError {
		Exp t = parseTerm();
		for (BinOp op = parseLowPrioOp(); op != null; op = parseLowPrioOp()) {
			t = new BinOpExp(t, parseTerm(), op);
		}
		return t;
	}
	
	public Exp parseTerm() throws ParseError {
		Exp f = parseFact();
		for (BinOp op = parseHighPrioOp(); op != null; op = parseHighPrioOp()) {
			f = new BinOpExp(f, parseFact(), op);
		}
		return f;
	}
	
	public Exp parseFact() throws ParseError {
		switch (tokens.currentToken()) {
			case NumberToken: {
				NumExp ne = new NumExp(tokens.getNumber());
				tokens.nextToken();
				return ne;
			}
			case MinusToken:
				tokens.nextToken();
				if (tokens.currentToken() == ArithmeticTokenizer.ArithmeticToken.NumberToken) {
					NumExp ne = new NumExp(-1 * tokens.getNumber());
					tokens.nextToken();
					return ne;
				} else {
					throw new ParseError("Number expected", tokens.currentToken().toString());
				}
			default:
				throw new ParseError("Factor expected", tokens.currentToken().toString());
		}
	}
}
